<template>
  <div>
    <el-alert
      title="最近5次运行时间"
      type="success"
      :closable="false"
      style="margin-bottom: 5px;"
    >
    </el-alert>
    <div v-if="isShow">
      <div
        v-for="(item, index) in resultList"
        :key="index"
        style="margin: 5px 0; padding: 8px; background-color: #f5f5f5; border-radius: 4px;"
      >
        {{ item }}
      </div>
    </div>
    <div v-else style="text-align: center; padding: 20px; color: #999;">
      计算中...
    </div>
  </div>
</template>

<script setup>
import { ref, watch, onMounted } from 'vue'

const props = defineProps({
  ex: {
    type: String,
    default: ''
  }
})

const isShow = ref(false)
const resultList = ref([])
const dateArr = ref([[], [], [], [], [], []])
const dayRule = ref('')
const dayRuleSup = ref('')

watch(() => props.ex, () => {
  expressionChange()
})

function expressionChange() {
  isShow.value = false
  resultList.value = []
  if (props.ex) {
    setTimeout(() => {
      getResult(props.ex)
    }, 100)
  }
}

function getResult(rule) {
  let ruleArr = rule.split(' ')
  if (ruleArr.length < 6 || ruleArr.length > 7) {
    resultList.value = ['表达式格式错误！']
    isShow.value = true
    return
  }
  
  // 获取当前时间
  let now = new Date()
  let year = now.getFullYear()
  
  // 解析各个时间字段
  getSecondArr(ruleArr[0])
  getMinArr(ruleArr[1])
  getHourArr(ruleArr[2])
  getDayArr(ruleArr[3])
  getMonthArr(ruleArr[4])
  getWeekArr(ruleArr[5])
  if (ruleArr.length === 7) {
    getYearArr(ruleArr[6], year)
  } else {
    getYearArr('*', year)
  }
  
  // 计算结果
  calculateResult()
}

function calculateResult() {
  let resultArr = []
  let nums = 0
  let now = new Date()
  let nYear = now.getFullYear()
  let nMonth = now.getMonth() + 1
  let nDay = now.getDate()
  let nHour = now.getHours()
  let nMin = now.getMinutes()
  let nSecond = now.getSeconds()
  
  let YDate = dateArr.value[5]
  let MDate = dateArr.value[4]
  let DDate = dateArr.value[3]
  let hDate = dateArr.value[2]
  let mDate = dateArr.value[1]
  let sDate = dateArr.value[0]
  
  // 重置索引函数
  function resetSecond() {
    sIdx = 0
    nSecond = 0
  }
  function resetMin() {
    mIdx = 0
    nMin = 0
  }
  function resetHour() {
    hIdx = 0
    nHour = 0
  }
  function resetDay() {
    Di = 0
    nDay = 1
  }
  function resetMonth() {
    Mi = 0
    nMonth = 1
  }
  
  // 获取当前时间在各数组中的索引
  let Yi = getIndex(YDate, nYear)
  let Mi = getIndex(MDate, nMonth)
  let Di = getIndex(DDate, nDay)
  let hIdx = getIndex(hDate, nHour)
  let mIdx = getIndex(mDate, nMin)
  let sIdx = getIndex(sDate, nSecond)
  
  // 循环计算
  goYear: for (let yi = Yi; yi < YDate.length; yi++) {
    let YY = YDate[yi]
    
    // 循环"月"数组
    goMonth: for (let mi = Mi; mi < MDate.length; mi++) {
      let MM = MDate[mi] < 10 ? '0' + MDate[mi] : MDate[mi]
      
      // 循环"日"数组
      goDay: for (let di = Di; di < DDate.length; di++) {
        let DD = DDate[di]
        let thisDD = DD < 10 ? '0' + DD : DD
        
        // 处理特殊日期规则
        if (dayRule.value === 'workDay') {
          // 工作日逻辑
          let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week')
          if (thisWeek === 1 || thisWeek === 7) {
            // 如果是周末，跳过
            continue
          }
        } else if (dayRule.value === 'lastDay') {
          // 月末最后一天
          if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
            while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
              DD--
              thisDD = DD < 10 ? '0' + DD : DD
            }
          }
        } else if (dayRule.value === 'weekDay') {
          // 指定星期几
          let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week')
          if (dayRuleSup.value.indexOf(thisWeek) < 0) {
            continue
          }
        } else if (dayRule.value === 'assWeek') {
          // 第几周的星期几
          let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week')
          if (dayRuleSup.value[1] >= thisWeek) {
            DD = (dayRuleSup.value[0] - 1) * 7 + dayRuleSup.value[1] - thisWeek + 1
          } else {
            DD = dayRuleSup.value[0] * 7 + dayRuleSup.value[1] - thisWeek + 1
          }
        } else if (dayRule.value === 'lastWeek') {
          // 最后一个星期几
          if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
            while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
              DD--
              thisDD = DD < 10 ? '0' + DD : DD
            }
          }
          let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week')
          if (dayRuleSup.value < thisWeek) {
            DD -= thisWeek - dayRuleSup.value
          } else if (dayRuleSup.value > thisWeek) {
            DD -= 7 - (dayRuleSup.value - thisWeek)
          }
        }
        
        DD = DD < 10 ? '0' + DD : DD
        
        // 循环"时"数组
        goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
          let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
          
          // 循环"分"数组
          goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
            let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi]
            
            // 循环"秒"数组
            goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
              let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si]
              
              // 添加当前时间
              if (MM !== '00' && DD !== '00') {
                resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
                nums++
              }
              
              // 如果条数满了就退出循环
              if (nums === 5) break goYear
              
              // 重置秒索引
              if (si === sDate.length - 1) {
                resetSecond()
                if (mi === mDate.length - 1) {
                  resetMin()
                  if (hi === hDate.length - 1) {
                    resetHour()
                    if (di === DDate.length - 1) {
                      resetDay()
                      if (mi === MDate.length - 1) {
                        resetMonth()
                        continue goYear
                      }
                      continue goMonth
                    }
                    continue goDay
                  }
                  continue goHour
                }
                continue goMin
              }
            }
          }
        }
      }
    }
  }
  
  // 判断结果
  if (resultArr.length === 0) {
    resultList.value = ['没有达到条件的结果！']
  } else {
    resultList.value = resultArr
    if (resultArr.length !== 5) {
      resultList.value.push('最近100年内只有上面' + resultArr.length + '条结果！')
    }
  }
  
  isShow.value = true
}

// 用于计算某位数字在数组中的索引
function getIndex(arr, value) {
  if (value <= arr[0] || value > arr[arr.length - 1]) {
    return 0
  } else {
    for (let i = 0; i < arr.length - 1; i++) {
      if (value > arr[i] && value <= arr[i + 1]) {
        return i + 1
      }
    }
  }
}

// 获取"年"数组
function getYearArr(rule, year) {
  dateArr.value[5] = getOrderArr(year, year + 100)
  if (rule !== undefined) {
    if (rule.indexOf('-') >= 0) {
      dateArr.value[5] = getCycleArr(rule, year + 100, false)
    } else if (rule.indexOf('/') >= 0) {
      dateArr.value[5] = getAverageArr(rule, year + 100)
    } else if (rule !== '*') {
      dateArr.value[5] = getAssignArr(rule)
    }
  }
}

// 获取"月"数组
function getMonthArr(rule) {
  dateArr.value[4] = getOrderArr(1, 12)
  if (rule.indexOf('-') >= 0) {
    dateArr.value[4] = getCycleArr(rule, 12, false)
  } else if (rule.indexOf('/') >= 0) {
    dateArr.value[4] = getAverageArr(rule, 12)
  } else if (rule !== '*') {
    dateArr.value[4] = getAssignArr(rule)
  }
}

// 获取"日"数组-主要为日期规则
function getWeekArr(rule) {
  // 只有当日期规则的两个值均为""时则表达日期是有选项的
  if (dayRule.value === '' && dayRuleSup.value === '') {
    if (rule.indexOf('-') >= 0) {
      dayRule.value = 'weekDay'
      dayRuleSup.value = getCycleArr(rule, 7, false)
    } else if (rule.indexOf('#') >= 0) {
      dayRule.value = 'assWeek'
      let matchRule = rule.match(/[0-9]{1}/g)
      dayRuleSup.value = [Number(matchRule[1]), Number(matchRule[0])]
      dateArr.value[3] = [1]
      if (dayRuleSup.value[1] === 7) {
        dayRuleSup.value[1] = 0
      }
    } else if (rule.indexOf('L') >= 0) {
      dayRule.value = 'lastWeek'
      dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0])
      dateArr.value[3] = [31]
      if (dayRuleSup.value === 7) {
        dayRuleSup.value = 0
      }
    } else if (rule !== '*' && rule !== '?') {
      dayRule.value = 'weekDay'
      dayRuleSup.value = getAssignArr(rule)
    }
  }
}

// 获取"日"数组-少量为日期规则
function getDayArr(rule) {
  dateArr.value[3] = getOrderArr(1, 31)
  dayRule.value = ''
  dayRuleSup.value = ''
  if (rule.indexOf('-') >= 0) {
    dateArr.value[3] = getCycleArr(rule, 31, false)
    dayRuleSup.value = 'null'
  } else if (rule.indexOf('/') >= 0) {
    dateArr.value[3] = getAverageArr(rule, 31)
    dayRuleSup.value = 'null'
  } else if (rule.indexOf('W') >= 0) {
    dayRule.value = 'workDay'
    dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0])
    dateArr.value[3] = [dayRuleSup.value]
  } else if (rule.indexOf('L') >= 0) {
    dayRule.value = 'lastDay'
    dayRuleSup.value = 'null'
    dateArr.value[3] = [31]
  } else if (rule !== '*' && rule !== '?') {
    dateArr.value[3] = getAssignArr(rule)
    dayRuleSup.value = 'null'
  } else if (rule === '*') {
    dayRuleSup.value = 'null'
  }
}

// 获取"时"数组
function getHourArr(rule) {
  dateArr.value[2] = getOrderArr(0, 23)
  if (rule.indexOf('-') >= 0) {
    dateArr.value[2] = getCycleArr(rule, 24, true)
  } else if (rule.indexOf('/') >= 0) {
    dateArr.value[2] = getAverageArr(rule, 23)
  } else if (rule !== '*') {
    dateArr.value[2] = getAssignArr(rule)
  }
}

// 获取"分"数组
function getMinArr(rule) {
  dateArr.value[1] = getOrderArr(0, 59)
  if (rule.indexOf('-') >= 0) {
    dateArr.value[1] = getCycleArr(rule, 60, true)
  } else if (rule.indexOf('/') >= 0) {
    dateArr.value[1] = getAverageArr(rule, 59)
  } else if (rule !== '*') {
    dateArr.value[1] = getAssignArr(rule)
  }
}

// 获取"秒"数组
function getSecondArr(rule) {
  dateArr.value[0] = getOrderArr(0, 59)
  if (rule.indexOf('-') >= 0) {
    dateArr.value[0] = getCycleArr(rule, 60, true)
  } else if (rule.indexOf('/') >= 0) {
    dateArr.value[0] = getAverageArr(rule, 59)
  } else if (rule !== '*') {
    dateArr.value[0] = getAssignArr(rule)
  }
}

// 根据传进来的min-max返回一个顺序的数组
function getOrderArr(min, max) {
  let arr = []
  for (let i = min; i <= max; i++) {
    arr.push(i)
  }
  return arr
}

// 根据规则中指定的零散值返回一个数组
function getAssignArr(rule) {
  let arr = []
  let assiginArr = rule.split(',')
  for (let i = 0; i < assiginArr.length; i++) {
    arr[i] = Number(assiginArr[i])
  }
  arr.sort(compare)
  return arr
}

// 根据一定算术规则计算返回一个数组
function getAverageArr(rule, limit) {
  let arr = []
  let agArr = rule.split('/')
  let min = Number(agArr[0])
  let step = Number(agArr[1])
  while (min <= limit) {
    arr.push(min)
    min += step
  }
  return arr
}

// 根据规则返回一个具有周期性的数组
function getCycleArr(rule, limit, status) {
  // status--表示是否从0开始（则从1开始）
  let arr = []
  let cycleArr = rule.split('-')
  let min = Number(cycleArr[0])
  let max = Number(cycleArr[1])
  if (min > max) {
    max += limit
  }
  for (let i = min; i <= max; i++) {
    let add = 0
    if (status === false && i % limit === 0) {
      add = limit
    }
    arr.push(Math.round(i % limit + add))
  }
  arr.sort(compare)
  return arr
}

// 比较数字大小（用于Array.sort）
function compare(value1, value2) {
  if (value2 - value1 > 0) {
    return -1
  } else {
    return 1
  }
}

// 格式化日期格式如：2017-9-19 18:04:33
function formatDate(value, type) {
  // 计算日期相关值
  let time = typeof value == 'number' ? new Date(value) : value
  let Y = time.getFullYear()
  let M = time.getMonth() + 1
  let D = time.getDate()
  let h = time.getHours()
  let m = time.getMinutes()
  let s = time.getSeconds()
  let week = time.getDay()
  // 如果传递了type的话
  if (type === undefined) {
    return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
  } else if (type === 'week') {
    // 在quartz中 1为星期日
    return week + 1
  }
}

// 检查日期是否存在
function checkDate(value) {
  let time = new Date(value)
  let format = formatDate(time)
  return value === format
}

onMounted(() => {
  expressionChange()
})
</script>

<style scoped>
/* 组件样式 */
</style>